package com.colmmcsky.twit2lj;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Vector;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Message;

import twitter4j.MediaEntity;
import twitter4j.Paging;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.URLEntity;
import twitter4j.conf.ConfigurationBuilder;

// This thread does the work of reading twitter posts and posting to LJ
public class PostWorker extends Thread {
	
	private class TwitterAccountNotFoundException extends Exception{
		private static final long serialVersionUID = 1L;}

	//private class NoTweetsYesterdayException extends Exception{
	//	private static final long serialVersionUID = 1L;}

	private boolean mDoDailyDigest;
	private boolean mDoMonthlyDigest;	
	private boolean mPrivateDailyPost;
	private boolean mPrivateMonthlyPost;
	private boolean mHidefDailyPost;
	private boolean mHidefMonthlyPost;
	private boolean mPrefixMyUsername;
	private boolean mPostRTs;
	private boolean mPostReplies;	
	private int mHour;
	private int mMinute;
	private String mTwitterAccount;
	private String mLivejournalAccount;
	private String mLivejournalPassword;
	private String mAuthChallenge;
	private String mAuthResponse;
	private Context mContext;
	private LogUpdater mLogUpdater;
	private HttpClient mHttpClient = new DefaultHttpClient();
	private HttpContext mLocalContext = new BasicHttpContext();

	private Twitter mTwitter = null;
	
	public PostWorker(LogUpdater logUpdater, Context context, boolean doDailyDigest, boolean doMonthlyDigest) {
		try {
			mDoDailyDigest = doDailyDigest;
			mDoMonthlyDigest = doMonthlyDigest;
	    	mLogUpdater = logUpdater;
	    	mContext = context;
			
	    	SharedPreferences preferences = context.getSharedPreferences(MainActivity.PrefsName, 0);
	    	if (mLogUpdater == null) {
	    		mLogUpdater = new LogUpdater(preferences, null);
	    	}
	    	mHour = preferences.getInt(MainActivity.HourKey, 4);
	    	mMinute = preferences.getInt(MainActivity.MinuteKey, 0);
	    	mTwitterAccount = preferences.getString(MainActivity.TwitterAccountKey, "");
	    	mLivejournalAccount = preferences.getString(MainActivity.LivejournalAccountKey, "");
	    	mLivejournalPassword = preferences.getString(MainActivity.LivejournalPasswordKey, "");	    	
	    	mPrivateDailyPost = preferences.getBoolean(MainActivity.PrivatePostKey, false);
	    	mPrivateMonthlyPost = preferences.getBoolean(MainActivity.PrivateMonthlyPostKey, true);	     	
	    	mHidefDailyPost = preferences.getBoolean(MainActivity.HidefPostKey, false);
	    	mHidefMonthlyPost = preferences.getBoolean(MainActivity.HidefMonthlyPostKey, true);	   	
	    	mPrefixMyUsername = preferences.getBoolean(MainActivity.PrefixMyUsernameKey, false);
	    	mPostRTs = preferences.getBoolean(MainActivity.PostRTsKey, false);
	    	mPostReplies = preferences.getBoolean(MainActivity.PostRepliesKey, false);
		} catch (Exception e) {
			if (mLogUpdater != null) {
				MainActivity.UpdateLog(mLogUpdater, "Exception in PostWorker Constructor " + e.getMessage());
			}
		}    	
	}

	public void run() {
		boolean ok = true;
		if (mDoDailyDigest) {
			ok = DailyDigest();
		}
		if (ok && mDoMonthlyDigest) {	
			MonthlyDigest();
		} 
	}

	public boolean DailyDigest(){
		// set retry for if we time out
		MainActivity.SetAlarm(mContext, mLogUpdater, true);
	
		try {
			Calendar startDate = Calendar.getInstance(); // start with today
			startDate.add(Calendar.DAY_OF_MONTH, -1); // subtract a day to get to yesterday
			startDate.set(Calendar.HOUR_OF_DAY, mHour); // set to posting time
			startDate.set(Calendar.MINUTE, mMinute);
			startDate.set(Calendar.SECOND, 0);
			startDate.set(Calendar.MILLISECOND, 0);
			
			UpdateLog("Starting, Getting Tweets");
			Vector<Vector<TwitterPost>> days = GetDaysBackToStartDate(startDate, null, mHidefDailyPost);
	        String html = "";
	        
	        if (days.isEmpty())  {
				UpdateLog("No Tweets Found.");
				
		        // set normal alarm
				MainActivity.SetAlarm(mContext, mLogUpdater, false);
				return true;
	        } else {   
				UpdateLog("Got Tweets");
				
		        // build each day
		        for (int j=0; j<days.size(); ++j) {
		        	Vector<TwitterPost> day = days.elementAt(j);	        	
		        	html += BuildHtmlDay(day, false, mHidefDailyPost);
		        }
		        
		        html += "\n\n<i>This post was automatically generated by " +
		        		"<a href=\"https://market.android.com/details?id=com.colmmcsky.twit2lj\">" + 
		        		"Twit2LJ <img src=\"http://farm3.static.flickr.com/2596/" + 
		        		"5850592678_f8e9d6638b_o.png\"></a></i>";

				UpdateLog("Authorizing LJ");
				GetAuthorizedForLj();
				UpdateLog("Posting to LJ");
				String result = PostEntryToLj(BuildHtmlDayTitle(days.firstElement()), html, mPrivateDailyPost).replace("\n", " ");
				if (result.toUpperCase().contains("SUCCESS OK")) {
					UpdateLog(result);
					UpdateLog("Success!");
				} else {
					UpdateLog(result);
					UpdateLog("FAILED!");
					return false; // and retry in a few minutes
				}
	        }
        } catch (TwitterAccountNotFoundException e) {
			if (mLogUpdater != null) {
				MainActivity.UpdateLog(mLogUpdater, "No Such Twitter account: " + e.getMessage());
			}
        } catch (Exception e) {
			if (mLogUpdater != null) {
				MainActivity.UpdateLog(mLogUpdater, "Exception in PostWorker.DailyDigest " + e.getMessage());
				return false; // and retry in a few minutes
			}
		}   
        
        // set normal alarm
		MainActivity.SetAlarm(mContext, mLogUpdater, false);
		
		return true;
	}

	private void MonthlyDigest(){
		// set retry for if we time out
		MainActivity.SetAlarm(mContext, mLogUpdater, true);
		
		try {
			Calendar startDate = Calendar.getInstance();
			startDate.add(Calendar.MONTH, -1);
			startDate.set(Calendar.DAY_OF_MONTH, 1);
			startDate.set(Calendar.HOUR_OF_DAY, 0);
			startDate.set(Calendar.MINUTE, 0);
			startDate.set(Calendar.SECOND, 0);
			startDate.set(Calendar.MILLISECOND, 0);			
			
			Calendar endDate = Calendar.getInstance();
			endDate.set(Calendar.DAY_OF_MONTH, 1);
			endDate.set(Calendar.HOUR_OF_DAY, 0);
			endDate.set(Calendar.MINUTE, 0);
			endDate.set(Calendar.SECOND, 0);
			endDate.set(Calendar.MILLISECOND, 0);
	
			UpdateLog("Getting Days...");
			Vector<Vector<TwitterPost>> days = GetDaysBackToStartDate(startDate, endDate, mHidefMonthlyPost);
	        
	        // count the total number of tweets
	        int tweetCount = 0;
	        for (int i=0; i<days.size(); ++i) {
	        	tweetCount += days.elementAt(i).size();
	        }
	        
	        // start off with a cut, and lowering the font size
	        String html = "<lj-cut text=\"" + tweetCount + " Tweets - " + days.size() + " Days\"> <font size=\"2\">\n";
			
	        // build each day
	        for (int j=0; j<days.size(); ++j) {
				UpdateLog("Building Day " + j);
	        	Vector<TwitterPost> day = days.elementAt(j);
	        	if (!day.isEmpty() && isPostBeforeDate(day.firstElement(), endDate)) {
	        		html += BuildHtmlDay(day, true, mHidefMonthlyPost);
	        	}
	        }
	        
	        // post to LJ
			GetAuthorizedForLj();
			UpdateLog("Posting to LJ...");
			String title = "Twit2LJ Monthly Digest for " 
				+ MonthString(startDate.get(Calendar.MONTH))
				+ " " + startDate.get(Calendar.YEAR);
			String result = PostEntryToLj(title, html, mPrivateMonthlyPost).replace("\n", " ");
			if (result.toUpperCase().contains("SUCCESS OK")) {
				UpdateLog(result);
				UpdateLog("Success!");
			} else {
				UpdateLog(result);
				UpdateLog("FAILED!");
				return; // and retry in a few minutes
			}
        } catch (TwitterAccountNotFoundException e) {
			if (mLogUpdater != null) {
				MainActivity.UpdateLog(mLogUpdater, "No Such Twitter account: " + e.getMessage());
			}
        } catch (Exception e) {
			if (mLogUpdater != null) {
				MainActivity.UpdateLog(mLogUpdater, "Exception in PostWorker.MonthlyDigest " + e.getMessage());
				return; // and retry in a few minutes
			}
		}        

        // set normal alarm
		MainActivity.SetAlarm(mContext, mLogUpdater, false);
	}
    
	private boolean isPostBeforeDate(TwitterPost post, Calendar date) {
		Calendar postDate = Calendar.getInstance();
		postDate.setTime(post.Date);
		
		return postDate.before(date);
	}
	
	private Vector<Vector<TwitterPost>> GetDaysBackToStartDate(Calendar startDate, Calendar endDate, boolean hidef)
			throws TwitterAccountNotFoundException {
		int page = 1;
		
		Vector<TwitterPost> posts = new Vector<TwitterPost>();
		posts.addAll(GetTwitterPosts(mTwitterAccount, page));
		Calendar calendar = Calendar.getInstance();
		
		if (posts.size() == 0) {	
    		return new Vector<Vector<TwitterPost>>();
		}
		
		// get all posts, going back to the start date		
		while (true) {			
			++page;
			
			if (isPostBeforeDate(posts.lastElement(), startDate)) {
				break;
			}
			
			if (posts.size() > 1200) {
        		UpdateLog("Too many tweets! (over 1200)");
				break;
			}

			Vector<TwitterPost> newPosts = GetTwitterPosts(mTwitterAccount, page);
			
			if (newPosts.size() < 2) {
				break;
			}
			
			posts.addAll(newPosts);
		}
		
		// split into days			
        Collections.reverse(posts);
        Vector<Vector<TwitterPost>> days = new Vector<Vector<TwitterPost>>();
        int lastDate = -1;
        Vector<TwitterPost> currentDay = null;
        for (int i=0; i<posts.size(); ++i) {
        	TwitterPost post = posts.elementAt(i);
        	
        	// see if we need to start a new day
			calendar.setTime(post.Date);
			int postDate = calendar.get(Calendar.DATE);
			
			// skip dates before the start date
			if (isPostBeforeDate(post, startDate)) {
	    		continue;
			}

			// skip dates not before the end date
			if (endDate != null && !isPostBeforeDate(post, endDate)) {
	    		continue;
			}

			// discard RTs if desired
			if (!mPostRTs && post.RawPost.startsWith("RT ")) {
				continue;
			}
			
			// discard @replies if desired
			if (!mPostReplies && post.RawPost.startsWith("@")) {
				continue;
			}
			
			// process any pictures attached to this post
			ProcessPics(post, hidef);
			
        	if (postDate != lastDate) {
        		lastDate = postDate;
        		currentDay = new Vector<TwitterPost>();
        		days.add(currentDay);
        	}
        	
        	if (currentDay == null) {
        		UpdateLog("!!NULLDAY!!");
        		break;
        	}
        	
        	currentDay.add(post);
        }
        
		return days;
	}

	private void UpdateLog(String string) {
        Message message = Message.obtain();			
        message.obj = string;
        mLogUpdater.sendMessage(message);	
	}
		
    private String GetPage(String url)
    {
    	try {
			// set up http client
	    	HttpGet httpGet = new HttpGet(url);
			HttpResponse response = mHttpClient.execute(httpGet, mLocalContext);
			HttpEntity entity = response.getEntity();
	
			return new String(EntityUtils.toByteArray(entity));
    	}
    	catch (Exception e) {
			e.printStackTrace();
			return e.getLocalizedMessage();
		}   
    }

    private String PostToLj(List<NameValuePair> nameValuePairs)
    {
        try {
	        HttpClient httpclient = new DefaultHttpClient();
	    	HttpPost httppost = new HttpPost("http://www.livejournal.com/interface/flat"); 
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

            // Execute HTTP Post Request
            HttpResponse response = httpclient.execute(httppost);
			HttpEntity entity = response.getEntity();
			return new String(EntityUtils.toByteArray(entity));
		} catch (Exception e) {
			e.printStackTrace();
			return e.getLocalizedMessage();
		}      	
    }
    
    public String md5(String s) {
        try {
            // Create MD5 Hash
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            digest.update(s.getBytes());
            byte messageDigest[] = digest.digest();
            
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < messageDigest.length; i++) {
                String h = Integer.toHexString(0xFF & messageDigest[i]);
                while (h.length() < 2)
                    h = "0" + h;
                hexString.append(h);
            }
            return hexString.toString();
            
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
    
    private String GetAuthorizedForLj()
    {
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);  
        nameValuePairs.add(new BasicNameValuePair("mode", "getchallenge"));   
		
        // ... 
        // challenge
        // xlkjdfi
        // expire_time
        // ...
        String response = PostToLj(nameValuePairs);
		
        int startIndex = response.indexOf("challenge\n") + "challenge\n".length();
        int endIndex = response.indexOf("\nexpire_time");
		mAuthChallenge = response.substring(startIndex, endIndex);
		mAuthResponse = md5(mAuthChallenge + md5(mLivejournalPassword));
		
		return mAuthChallenge;
    }
	
    private String PostEntryToLj(String subject, String body, boolean privatePost)
    {
    	Calendar now = Calendar.getInstance();
    	String year = Integer.toString(now.get(Calendar.YEAR));
    	String month = Integer.toString(now.get(Calendar.MONTH) + 1);
    	String day = Integer.toString(now.get(Calendar.DAY_OF_MONTH));
    	String hour = Integer.toString(now.get(Calendar.HOUR_OF_DAY));
    	String minute = Integer.toString(now.get(Calendar.MINUTE));
    	
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);  
        nameValuePairs.add(new BasicNameValuePair("mode", "postevent"));  
        nameValuePairs.add(new BasicNameValuePair("user", mLivejournalAccount));    
        nameValuePairs.add(new BasicNameValuePair("auth_method", "challenge"));  
        nameValuePairs.add(new BasicNameValuePair("auth_challenge", mAuthChallenge));  
        nameValuePairs.add(new BasicNameValuePair("auth_response", mAuthResponse));  	
        nameValuePairs.add(new BasicNameValuePair("security", privatePost ? "private" : "public"));  	        
        nameValuePairs.add(new BasicNameValuePair("subject", subject));  
        nameValuePairs.add(new BasicNameValuePair("event", body));  
        nameValuePairs.add(new BasicNameValuePair("year", year));  
        nameValuePairs.add(new BasicNameValuePair("mon", month));  
        nameValuePairs.add(new BasicNameValuePair("day", day));  
        nameValuePairs.add(new BasicNameValuePair("hour", hour));  
        nameValuePairs.add(new BasicNameValuePair("min", minute));  
		return PostToLj(nameValuePairs); 	
    }

    private String DayString(int i) {
    	if (i == Calendar.SUNDAY)    return "Sunday";
    	if (i == Calendar.MONDAY)    return "Monday";
    	if (i == Calendar.TUESDAY)   return "Tuesday";
    	if (i == Calendar.WEDNESDAY) return "Wednesday";
    	if (i == Calendar.THURSDAY)  return "Thursday";
    	if (i == Calendar.FRIDAY)    return "Friday";
    	if (i == Calendar.SATURDAY)  return "Saturday";
    	return "???";
    }

    private String MonthString(int i) {
    	if (i == Calendar.JANUARY) return "January";
    	if (i == Calendar.FEBRUARY) return "February";
    	if (i == Calendar.MARCH) return "March";
    	if (i == Calendar.APRIL) return "April";
    	if (i == Calendar.MAY) return "May";
    	if (i == Calendar.JUNE) return "June";
    	if (i == Calendar.JULY) return "July";
    	if (i == Calendar.AUGUST) return "August";
    	if (i == Calendar.SEPTEMBER) return "September";
    	if (i == Calendar.OCTOBER) return "October";
    	if (i == Calendar.NOVEMBER) return "November";
    	if (i == Calendar.DECEMBER) return "December";
    	return "???";
    }
    
    private String BuildHtmlDayTitle(Vector<TwitterPost> posts) {
    	String html = "";
    	
    	if (posts.size() == 0) return html;
    	
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(posts.firstElement().Date);
    	
    	// <p><big>From Twitter, Wednesday, 12-10-2010</big>
    	return "From Twitter, " + 
    		DayString(calendar.get(Calendar.DAY_OF_WEEK)) + ", " + 
    		(calendar.get(Calendar.MONTH)+1) + "-" +
    		calendar.get(Calendar.DAY_OF_MONTH) + "-" +
    		calendar.get(Calendar.YEAR);		    	
    }	
    
    private String BuildHtmlDay(Vector<TwitterPost> posts, 
    		boolean includeTitle, 
    		boolean hidef) {
    	String html = "";
    	
    	if (posts.size() == 0) return html;
    	
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(posts.firstElement().Date);
    	
    	if (includeTitle) {
	    	// <p><big>From Twitter, Wednesday, 12-10-2010</big>
	    	html = "\n<p><big>From Twitter, " + 
	    		DayString(calendar.get(Calendar.DAY_OF_WEEK)) + ", " + 
	    		(calendar.get(Calendar.MONTH)+1) + "-" +
	    		calendar.get(Calendar.DAY_OF_MONTH) + "-" +
	    		calendar.get(Calendar.YEAR) + "</big>";
	    	
	    	html += "\n<hr>";
    	} else {
	    	html += "\n";
    	}
    	
    	for (int i=0; i<posts.size(); ++i) {
    		TwitterPost post = posts.elementAt(i);
        	calendar.setTime(post.Date);

        	if (includeTitle) {
        		html += "<p>";
        	} else {
            	html += "\n\n";
        	}
        	
        	String author = "";
        	if (!post.Author.equalsIgnoreCase(mTwitterAccount) || mPrefixMyUsername) {
        		author = "<b>@"+ post.Author + ":</b> ";
        	}
        	
        	int hour = calendar.get(Calendar.HOUR);
        	if (hour == 0) {
        		hour = 12;
        	}
        	
    		// 12:34 PM - <b>Author:</b> twitter post. http://flic.kr/p/8ZJGJz
        	html += 
				hour + ":" +
				(calendar.get(Calendar.MINUTE) < 10 ? "0" : "") +
				calendar.get(Calendar.MINUTE) + " " +
				(calendar.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM") + " - " +				
				author +
				post.Post;
        	
        	// add expanded links, in brackets
        	for (int j=0; j<post.MediaUrls.size(); ++j) {
        		html += "\n [ " + post.MediaUrls.get(j) + " ]";
        	}
        	for (int j=0; j<post.LinkUrls.size(); ++j) {
        		html += "\n [ " + post.LinkUrls.get(j) + " ]";
        	}
        	
        	// add pics
        	for (int j=0; j<post.Pics.size(); ++j) {
        		html += BuildHtmlForPicture(post.Pics.elementAt(j), includeTitle, hidef);
        	}
    	}

    	if (includeTitle) {
    		html += "\n\n";
    	}
    	
		return html;    	
    }
    
    private String BuildHtmlForPicture(PictureInfo pic, boolean includeTitle, boolean hidef) {
    	String html = "";

    	if (pic.ImageUrl != "") {
    		int height=0;
    		int width=0;
    		
    		if (pic.Ratio > 0) {
	    		if (pic.Ratio > 1) {
	    			height = 400;
	    			width = (int) (height * pic.Ratio);
	    		} else {
	    			width = 400;
	    			height = (int) (width / pic.Ratio);
	    		}
    		}

        	if (includeTitle) {
        		html += "<br>";
        	} else {
            	html += "\n";
        	}
        	
    		if (hidef && height > 0 && width > 0) {
	    		// <br><img src="http://farm6....jpg" height="400" width="534">        	
        		html += "<img src=\"" + 
        			pic.ImageUrl + "\" height=\"" +
        			height + "\" width=\"" +
        			width + "\">";
    		} else {
	    		// <br><a href="http://flic.kr/2342"><img src="http://farm6....jpg"></a>        	
        		html += "<a href=\""
        			+ pic.PageUrl
        			+ "\"><img src=\""
        			+ pic.ImageUrl 
        			+ "\"></a>";
    		}
    			
    		if (pic.Description != "") {
            	if (includeTitle) {
            		html += "<br>";
            	} else {
                	html += "\n";
            	}
    			// <br>Description of the picture. 
    			html += pic.Description;
    		}
    	}	
    	
    	return html;
    }

    private Vector<TwitterPost> GetTwitterPosts(String user, int page)
			throws TwitterAccountNotFoundException {
		UpdateLog("  page " + page + "...");
		
        try {
	    	if (mTwitter == null) {
	    		ConfigurationBuilder builder;
	
	            builder = new ConfigurationBuilder();
	            builder.setUseSSL(true);
	            builder.setApplicationOnlyAuthEnabled(true);
	            
	            String browserConsumerKey = xxx;
	            String browserConsumerSecret = xxx;
	            
	            builder.setOAuthConsumerKey(browserConsumerKey).setOAuthConsumerSecret(browserConsumerSecret);
	            mTwitter = new TwitterFactory(builder.build()).getInstance();    
				mTwitter.getOAuth2Token();        
	    	}

			List<Status> statuses = mTwitter.getUserTimeline(user, new Paging(page));
	    	Vector<TwitterPost> posts = new Vector<TwitterPost>();
	    	
	    	for (int i=0; i<statuses.size(); ++i) {
	    		Status status = statuses.get(i);
    			TwitterPost post = new TwitterPost();
    			
    			post.Status = status;
    			post.RawPost = status.getText();
    			post.Post = status.getText();
    			post.Date = status.getCreatedAt();
    			post.Author = mTwitterAccount;
    			
    			Status retweetedStatus = status.getRetweetedStatus();
    			if (retweetedStatus != null) {
    				post.Status = retweetedStatus;
        			post.Post = retweetedStatus.getText();
        			post.Author = retweetedStatus.getUser().getScreenName();
    			}
    			
    			MediaEntity[] mediaEntities = post.Status.getMediaEntities();
    			for (int j=0; j<mediaEntities.length; ++j) {
    				post.MediaUrls.add(mediaEntities[j].getMediaURL());
    			}

    			URLEntity[] urlEntities = post.Status.getURLEntities();
    			for (int j=0; j<urlEntities.length; ++j) {
    				post.LinkUrls.add(urlEntities[j].getExpandedURL());
    			}
    			
    			posts.add(post);
	    	}
	    	
	    	return posts;
		} catch (TwitterException e) {
    		throw new TwitterAccountNotFoundException();
		}        
    }
    
    private void ProcessPics(TwitterPost post, boolean hidef) {    	
    	// handle Flickr pics
    	for (int i=0; i<post.LinkUrls.size(); ++i){
    		String url = post.LinkUrls.get(i);
    		if (url.contains("flic.kr")) {
    			post.Pics.add(FlickrParse(url, hidef));
    		}
    	}

    	// handle twitter media pics
    	for (int i=0; i<post.MediaUrls.size(); ++i){
    		post.Pics.add(TwitterPicParse(post.MediaUrls.get(i)));    		
    	}    	
    }
    
    private long Base58Decode(String snipcode) {
	    String alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
	    int num = snipcode.length() ;
	    long decoded = 0 ;
	    long multi = 1 ;
	    for ( int i = (num-1) ; i >= 0 ; i-- ) {
	    	char c = snipcode.charAt(i);
	    	if (!alphabet.contains("" + c)) {
	    		break;
	    	}
	        decoded = decoded + multi * alphabet.indexOf(c) ;
	        multi = multi * alphabet.length() ;
	    }
	    return decoded;
    }

    private PictureInfo TwitterPicParse(String url)
    {
    	PictureInfo pic = new PictureInfo();
    	pic.Ratio = 0;
    	pic.ImageUrl = url;
    	pic.PageUrl = url;
    	return pic;
    }
    
    private PictureInfo FlickrParse(String url, boolean hidef) {
    	PictureInfo pic = new PictureInfo();
    	
    	String flickrStartString = "http://flic.kr/p/";
    	int flickrStart;
    	int flickrEnd;
    	
    	if (url.contains(flickrStartString)) {
			flickrStart = url.indexOf(flickrStartString) + flickrStartString.length();
			flickrEnd = url.length();
    	} else  {
    		return pic;
    	}
    	
    	try {
    		String selectedSize = hidef ? "Original" : "Medium";	    		
    		
			String base58id = url.substring(flickrStart, flickrEnd);
			String base10id = new Long(Base58Decode(base58id)).toString();

			pic.PageUrl = "http://flic.kr/p/" + base58id; 
			
			UpdateLog("Getting Flickr pic:" + base58id);
			
			String request = "http://api.flickr.com/services/rest/?method=flickr.photos.getSizes" + 
							"&api_key=398a63ea6cafb7f708f89a4d951b76c4&photo_id=" + base10id;
			
			String xml = GetPage(request);
			
			// <size label="Original" width="2592" height="1944" source="http://farm6.static.flickr....jpg" 
			// <size label="Original" width="
			//		width
			// " height="
			//		height
			// " source="
			// 		source
			// "
			
			String widthStartString = "<size label=\"" + selectedSize + "\" width=\"";		
			String heightStartString = "height=\"";		
			String sourceStartString = "source=\"";
			
			int widthStart = xml.indexOf(widthStartString, 0) + widthStartString.length();
			int widthEnd = xml.indexOf("\"", widthStart);
			
			int heightStart = xml.indexOf(heightStartString, widthEnd) + heightStartString.length();
			int heightEnd = xml.indexOf("\"", heightStart);
			
			int sourceStart = xml.indexOf(sourceStartString, heightEnd) + sourceStartString.length();
			int sourceEnd = xml.indexOf("\"", sourceStart);
			
			String widthStr = xml.substring(widthStart, widthEnd);
			String heightStr = xml.substring(heightStart, heightEnd);
			String source = xml.substring(sourceStart, sourceEnd);
			
			double width = new Double(widthStr).doubleValue();
			double height = new Double(heightStr).doubleValue();

			pic.ImageUrl = source;
			pic.Ratio = width / height;
			
			request = "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo" + 
							"&api_key=398a63ea6cafb7f708f89a4d951b76c4&photo_id=" + base10id;
			
			xml = GetPage(request);
			
			// <description>Since ... ended up in first class.</description> 
			
			String descStartString = "<description>";
			String descEndString = "</description>";
			
			if (xml.contains(descStartString)) {
				int descStart = xml.indexOf(descStartString) + descStartString.length();
				int descEnd = xml.indexOf(descEndString, descStart);
				
				if (descStart < descEnd) {
					pic.Description = xml.substring(descStart, descEnd);
				}
			} else {
				pic.Description = "";
			}
	    }
    	catch (Exception e) {
			e.printStackTrace();
			pic.PageUrl = "error";
			pic.ImageUrl = "error";
			pic.Ratio = 1;
			pic.Description = "error";
		}
    	
    	return pic;
    }
    
    private class PictureInfo {
		public String ImageUrl = "";
		public String PageUrl = "";
		public String Description = "";
		public double Ratio = 1;    	
    }

	private class TwitterPost {
		public String RawPost = "";
		public String Post = "";
		public String Author = "";
		public Date Date;
		public Status Status;
		
		public Vector<PictureInfo> Pics = new Vector<PictureInfo>();
		public Vector<String> MediaUrls = new Vector<String>();
		public Vector<String> LinkUrls = new Vector<String>();	
	}
}

